package Test;

import static org.junit.Assert.*;
import interfaces.ErrorReport;
import interfaces.Pointer;
import interfaces.PointerReader;
import interfaces.PointerWriter;
import interfaces.ScriptInserter;
import interfaces.ScriptReader;
import interfaces.ErrorReport.ErrorType;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import Calculate.ByteCalc;
import Calculate.ScriptFormat;
import Pointers.JPointerFactory;
import Pointers.SnesPointer;
import Pointers.JPointerFactory.PointerType;
import Script.JPointerReader;
import Script.JPointerWriter;
import Script.JScriptInserter;
import Script.JScriptReader;
import Script.Offset;
import Script.JTableFile;

public class JScriptInserterTest {
	private File testFile;
	private File pointerFile;
	private String fileUrl;
	private ArrayList<ErrorReport> err;
	private long header;
	private String separator = System.getProperty("line.separator");
	
	@Before
	public void setup() {
		fileUrl = "testFiles/gameTest/tempPrint.smc";
		header = 0;
	}
	
	@Test
	/**
	 * Prints a single piece of text to the first
	 * offset (0) in a file. File size is lower than
	 * text. Text is 5 characters long.
	 */
	public void printSingleTextToSingleFirstOffset() throws IOException {
		JTableFile table = new JTableFile(
			new File("testFiles/testTableFiles/ff3_only_upper_and_lower.tbl"));
		File script = new File("testFiles/testScripts/Script5.txt");
		
		/* Add empty pointer offset list. */
		ArrayList<Offset> offsets = new ArrayList<Offset>();
		
		testFile = new File(fileUrl);
		testFile.createNewFile();
		
		ScriptInserter in = new JScriptInserter(testFile);
			
		err = in.insert(new Offset(0), 
				  new Offset(17), 
				  script,
				  header,
				  offsets, 
				  PointerType._2Byte_littleEndian,
				  table,
				  0, (byte) 0);	
		
		assertTrue(err.size() == 0);
		assertEquals("Clean", read(testFile, table, 0, 5));
	}
	
	/**
	 * Prints the script to offset 5 inside the file.
	 * @throws IOException
	 */
	@Test
	public void printSingleTextToOffsetThatIsntFirst() throws IOException {
		JTableFile table = new JTableFile(
				new File("testFiles/testTableFiles/ff3_only_upper_and_lower.tbl"));
		File script = new File("testFiles/testScripts/Script5.txt");
			
		/* Add empty pointer offset list. */
		ArrayList<Offset> offsets = new ArrayList<Offset>();
			
		/* Create file. */
		testFile = new File(fileUrl);
		testFile.createNewFile();
			
		/* Extend file size, since program doesn't allow writing outside. */
		RandomAccessFile ran = new RandomAccessFile(testFile, "rw");
		ran.seek(20);
		ran.write(0);
		ran.close();
			
		ScriptInserter in = new JScriptInserter(testFile);
				
		err = in.insert(new Offset(5), 
					  new Offset(10), 
					  script,
					  header,
					  offsets, 
					  PointerType._2Byte_littleEndian,
					  table,
					  0, (byte) 0);	
			
		assertTrue(err.size() == 0);
		assertEquals("Clean", read(testFile, table, 5, 5));
	}
	
	/* Error handling. */
	
	@Test
	public void givesWarningWithMissingCharacters() throws FileNotFoundException, IOException {
		JTableFile 			table = new JTableFile(
								new File("testFiles/testTableFiles/ff3_only_upper_and_lower.tbl"), "ISO-8859-1");
		File 				script = new File("testFiles/testScripts/Script6.txt");
			
		/* Add offset pointing to 5 */
		ArrayList<Offset> 	offsets = new ArrayList<Offset>();
		offsets.add(new Offset(5));
			
		/* Create file. */
		testFile = new File(fileUrl);
		testFile.createNewFile();
			
		/* Extend file size, since program doesn't allow writing outside. */
		RandomAccessFile ran = new RandomAccessFile(testFile, "rw");
		ran.seek(20);
		ran.write(0);
		ran.close();
			
		ScriptInserter in = new JScriptInserter(testFile);
				
		err = in.insert(new Offset(0), 
					  new Offset(10), 
					  script,
					  header,
					  offsets, 
					  PointerType._2Byte_littleEndian,
					  table,
					  0, (byte) 0);	
			
		assertTrue(err.size() == 1);
	}
	
	
	@Test
	public void givesWarningWithScriptOutsidePrintRange() throws IOException {
		JTableFile table = new JTableFile(
				new File("testFiles/testTableFiles/ff3_only_upper_and_lower.tbl"));
			File script = new File("testFiles/testScripts/Script5.txt");
			
			/* Add empty pointer offset list. */
			ArrayList<Offset> offsets = new ArrayList<Offset>();
			
			testFile = new File(fileUrl);
			testFile.createNewFile();
			
			ScriptInserter in = new JScriptInserter(testFile);
				
			err = in.insert(new Offset(0), 
					  new Offset(4), 
					  script,
					  header,
					  offsets, 
					  PointerType._2Byte_littleEndian,
					  table,
					  0, (byte) 0);	

			assertTrue(err.size() == 1);
			assertEquals(err.get(0).getType(), ErrorType.ERROR);
			assertEquals("Clea", read(testFile, table, 0, 4));
			
			//5th byte should be unknown
			assertEquals("<$00>", read(testFile, table, 5, 1));
	}
	
	@Test
	public void givesErrorWithFaultyScript() throws IOException {
		JTableFile table = new JTableFile(
				new File("testFiles/testTableFiles/ff3_only_upper_and_lower.tbl"));
		File script = new File("testFiles/testScripts/Script10.txt");
		RandomAccessFile read;
		long fileLength;
			
		/* Add empty pointer offset list. */
		ArrayList<Offset> offsets = new ArrayList<Offset>();
		
		/* Clear file contents. */
		if (testFile != null && testFile.exists()) {
			testFile.delete();
		}
		testFile = new File(fileUrl);
		testFile.createNewFile();
			
		ScriptInserter in = new JScriptInserter(testFile);
		read = new RandomAccessFile(testFile, "r");
		
		try {
		/* Print 10 bytes, despite scriptbeing longer than that. */
		err = in.insert(new Offset(0), 
					  new Offset(10), 
					  script, 
					  header,
					  offsets, 
					  PointerType._2Byte_littleEndian,
					  table,
					  0, (byte) 0);
		} catch(NumberFormatException e) {
			fileLength = read.length();
			read.close();

			assertEquals(fileLength, 0);
		}
	
	}
	
	@Test
	public void canReadAndRepointSinglePointer() throws IOException {
		JTableFile 	table = new JTableFile(
			new File("testFiles/testTableFiles/ff3_only_upper_and_lower.tbl"));
		File 		script = new File("testFiles/testScripts/Script7.txt");
		PointerReader read;
		
		Offset 		off1 = new Offset(4); // Offset to point at
		Offset 		fOff = new Offset(0); // Offset to be stored in file
		Pointer 	p = new SnesPointer(1, off1, fOff);
		
		HashMap<Integer, Pointer> pList = new HashMap<Integer, Pointer>();
		ArrayList<Offset> pOffList = new ArrayList<Offset>();
		
		pList.put(1, p);
		
		/* 
		 * Begin by setting up the file, writing the pointer value to it.
		 * Then inserting the script text.
		 */
		testFile = new File(fileUrl);
		testFile.createNewFile();
		
		ScriptInserter in = new JScriptInserter(testFile);
		
		/* Write pointer at 0, pointing to 4. */
		JPointerWriter.write(testFile, pList);
		
		/* Write script at position 4. */
		err = in.insert(new Offset(4), 
				  new Offset(10), 
				  script, 
				  header,
				  pOffList, 
				  PointerType._2Byte_littleEndian,
				  table,
				  0, (byte) 0);
		
		/*
		 * Pointer and script is inserted manually
		 * to ensure test results.
		 */
		
		/** ----------- Test begins here ----------- */
		
		/* 
		 * Insert script 5 again, at position 5. Pointer at 0
		 * shold now point to 5.
		 */
		
		/* Write script at position 5. */
		pOffList.add(fOff);
		err = in.insert(new Offset(5), 
				  new Offset(10), 
				  script, 
				  header,
				  pOffList, 
				  PointerType._2Byte_littleEndian,
				  table,
				  0, (byte) 0);
		
		/* check script insertion. */
		assertEquals(read(testFile, table, 5, 5), "Clean");
		
		/* Check pointer re-pointing. */
		read = new JPointerReader(testFile, pOffList, new JPointerFactory(PointerType._2Byte_littleEndian));
		pList = read.getPointers();
		
		assertEquals(pList.size(), 1);
		assertEquals(pList.get(1).getValue(), new Long(5));
		
	}
	
	
	@Test
	public void canReadAndRepointTwoSeparatePointers() throws IOException {
		JTableFile 	table = new JTableFile(
			new File("testFiles/testTableFiles/ff3_only_upper_and_lower.tbl"));
		File 		script = new File("testFiles/testScripts/Script8.txt");
		PointerReader read;
		
		Offset 		off1 = new Offset(4); // Offset to point at
					off1.switchEndian();
		Offset 		off2 = new Offset(6); // Offset to point at
					off2.switchEndian();
		Offset 		fOff = new Offset(0); // Offset of off1 to be stored in file
		Offset 		fOff2 = new Offset(700); // Offset of off2 to be stored in file.
		Pointer 	p = new SnesPointer(1, off1, fOff);
		Pointer 	p2 = new SnesPointer(2, off2, fOff2);
		
		HashMap<Integer, Pointer> pList = new HashMap<Integer, Pointer>();
		ArrayList<Offset> pOffList = new ArrayList<Offset>();
		
		pList.put(1, p);
		pList.put(2, p2);
		
		/* 
		 * Begin by setting up the file, writing the pointer value to it.
		 * Then inserting the script text.
		 */
		testFile = new File(fileUrl);
		testFile.createNewFile();
		
		ScriptInserter in = new JScriptInserter(testFile);
		
		/*
		 * Write pointer at 0, pointing to 4. 
		 * Write pointer at 12, pointing to 6.
		 */
		JPointerWriter.write(testFile, pList);
		
		/* Write script at position 4. */
		err = in.insert(new Offset(4), 
				  new Offset(10), 
				  script, 
				  header,
				  pOffList, 
				  PointerType._2Byte_littleEndian,
				  table,
				  0, (byte) 0);
		
		/*
		 * Pointer and script is inserted manually
		 * to ensure test results.
		 */
		
		/** ----------- Test begins here ----------- */
		
		/* 
		 * Insert script 8 again, at position 5. Pointer at 0
		 * shold now point to 5, at 12 should point to 7.
		 */
		
		/* Write script at position 5. */
		pOffList.add(fOff);
		pOffList.add(fOff2);
		err = in.insert(new Offset(385), 
				  new Offset(394), 
				  script, 
				  header,
				  pOffList, 
				  PointerType._2Byte_littleEndian,
				  table,
				  0, (byte) 0);
		
		/* check script insertion. */
		assertEquals(read(testFile, table, 385, 5), "Clean");
		
		/* Check pointer re-pointing. */
		read = new JPointerReader(testFile, pOffList, new JPointerFactory(PointerType._2Byte_littleEndian));
		pList = read.getPointers();
		
		/* Check pointer number */
		assertEquals(pList.size(), 2);
		
		/* Pointer integrity */
		assertEquals(pList.get(1).getFileOffset().getValue(), 0);
		assertEquals(pList.get(2).getFileOffset().getValue(), 700);
		
		/* Pointer values. */
		assertEquals(pList.get(1).getValue(), new Long(385));
		assertEquals(pList.get(2).getValue(), new Long(387));
		
	}
	
	
	@Test
	public void onlyPrintsPartOfScriptWithinPrintRange() throws IOException {
		JTableFile table = new JTableFile(
				new File("testFiles/testTableFiles/ff3_only_upper_and_lower.tbl"));
		File script = new File("testFiles/testScripts/Script6.txt");
		RandomAccessFile read;
		long fileLength;
			
		/* Add empty pointer offset list. */
		ArrayList<Offset> offsets = new ArrayList<Offset>();
		
		/* Clear file contents. */
		if (testFile != null && testFile.exists()) {
			testFile.delete();
		}
		testFile = new File(fileUrl);
		testFile.createNewFile();
			
		ScriptInserter in = new JScriptInserter(testFile);
		read = new RandomAccessFile(testFile, "r");
		
		/* Print 10 bytes, despite scriptbeing longer than that. */
		err = in.insert(new Offset(0), 
					  new Offset(10), 
					  script, 
					  header,
					  offsets, 
					  PointerType._2Byte_littleEndian,
					  table,
					  0, (byte) 0);
		
		fileLength = read.length();
		read.close();
		
		assertEquals(fileLength, 10);
		assertEquals(read(testFile, table, 0, 10), "Illegal ch");
		
	}
	
	
	@Test
	public void printsNilValuesWhenScriptIsShorterThanPrintRange() throws IOException {
		JTableFile table = new JTableFile(
				new File("testFiles/testTableFiles/ff3_only_upper_and_lower.tbl"));
		File shortScript = new File("testFiles/testScripts/Script5.txt");
		File longScript = new File("testFiles/testScripts/Script6.txt");
		RandomAccessFile read;
		byte[] nilBytes;
		boolean isNil = true;
			
		/* Add empty pointer offset list. */
		ArrayList<Offset> offsets = new ArrayList<Offset>();
		
		/* Clear file contents. */
		if (testFile != null && testFile.exists()) {
			testFile.delete();
		}
		testFile = new File(fileUrl);
		testFile.createNewFile();
			
		ScriptInserter in = new JScriptInserter(testFile);
		read = new RandomAccessFile(testFile, "r");
		
		/* Print 10 bytes, despite scriptbeing longer than that. */
		err = in.insert(new Offset(0), 
					  new Offset(10), 
					  longScript, 
					  header,
					  offsets, 
					  PointerType._2Byte_littleEndian,
					  table,
					  0, (byte) 0);
		
		/* Print a 5-byte script. Last 5 bytes should now be 0. */
		err = in.insert(new Offset(0), 
				  new Offset(10), 
				  shortScript,
				  header,
				  offsets, 
				  PointerType._2Byte_littleEndian,
				  table,
				  0, (byte) 0);
		
		nilBytes = new byte[5];
		read.seek(5);
		read.read(nilBytes);
		read.close();
		
		/* Check if there's a non-0 byte at position 5-10. */
		for (int i = 0; i < nilBytes.length; i++) {
			if (nilBytes[i] != 0) {
				isNil = false;
			}
		}
		
		assertTrue(isNil);
	}
	
	@Test
	public void canInsertSpecialCharacters() throws FileNotFoundException, IOException {
		JTableFile table = new JTableFile(
				new File("testFiles/testTableFiles/ff3_full.tbl"));
		File script = new File("testFiles/testScripts/Script11.txt");
		ScriptReader tReader = new JScriptReader(script, table.getCharset());
		String original;
		String sep = System.getProperty("line.separator");
			
		/* Add empty pointer offset list. */
		ArrayList<Offset> offsets = new ArrayList<Offset>();
		
		/* Clear file contents. */
		if (testFile != null && testFile.exists()) {
			testFile.delete();
		}
		
		/* Create file. */
		testFile = new File(fileUrl);
		testFile.createNewFile();
		
		/* Read the original string from the script. ArrayList -> TextScriptArea -> Text */
		original = tReader.read(0).get(0).getText();
		
		/* Insert the script into a file. */
		ScriptInserter in = new JScriptInserter(testFile);
		err = in.insert(new Offset(0), 
					  new Offset(15), 
					  script, 
					  header,
					  offsets, 
					  PointerType._2Byte_littleEndian,
					  table,
					  0, (byte) 0);

		/* Compare the file-read data with the string from the script. */
		/**
		 * NOTE! The original string contains no linebreak, since
		 * the script doesn't have one. But the tablefile will
		 * always give a linebreak after an <end> tag, so +sep is
		 * added to compensate for this.
		 */
		assertEquals(original + sep, read(testFile, table, 0, 12));
	}
	
	
	@Test
	/**
	 * Header is 10 (0 - 9) bytes.
	 * Text starts at 25.
	 * Pointer located at 10,  points at 15 (25 - 10)
	 * After insertion at 30, pointer should point at 20.
	 * 
	 * Script is 5 bytes.
	 */
	public void canAdjustPointersWithHeaderValue() throws FileNotFoundException, IOException {
		JTableFile 	table = new JTableFile(
				new File("testFiles/testTableFiles/ff3_only_upper_and_lower.tbl"));
			File 		script = new File("testFiles/testScripts/Script7.txt");
			PointerReader read;
			
			Offset 		off1 = new Offset(15); // Offset to point at
			Offset 		fOff = new Offset(10); // Offset to be stored in file
			Pointer 	p = new SnesPointer(1, off1, fOff);
			long		tempHeader = 10;
			
			HashMap<Integer, Pointer> pList = new HashMap<Integer, Pointer>();
			ArrayList<Offset> pOffList = new ArrayList<Offset>();
			
			pList.put(1, p);
			
			/* 
			 * Begin by setting up the file, writing the pointer value to it.
			 * Then inserting the script text.
			 */
			testFile = new File(fileUrl);
			testFile.createNewFile();
			
			ScriptInserter in = new JScriptInserter(testFile);
			
			/* Write pointer at 10, pointing to 15. */
			JPointerWriter.write(testFile, pList);
			
			/* Write script at position 25. */
			err = in.insert(new Offset(25), 
					  new Offset(30), 
					  script, 
					  tempHeader,
					  pOffList, 
					  PointerType._2Byte_littleEndian,
					  table,
					  0, (byte) 0);
			
			/*
			 * Pointer and script is inserted manually
			 * to ensure test results.
			 */
			
			/** ----------- Test begins here ----------- */
			
			/* 
			 * Insert script 7 again, at position 30. Pointer at 10
			 * shold now point to 20.
			 */
			
			/* Write script at position 30. */
			pOffList.add(fOff);
			err = in.insert(new Offset(30), 
					  new Offset(35), 
					  script, 
					  tempHeader,
					  pOffList, 
					  PointerType._2Byte_littleEndian,
					  table,
					  0, (byte) 0);
			
			/* check script insertion. */
			assertEquals(read(testFile, table, 30, 5), "Clean");
			
			/* Check pointer re-pointing. */
			read = new JPointerReader(testFile, pOffList, new JPointerFactory(PointerType._2Byte_littleEndian));
			pList = read.getPointers();
			
			assertEquals(pList.size(), 1);
			assertEquals(pList.get(1).getValue(), new Long(20));
	}
	
	
	@Test
	public void canInsertHexadeciamlTagsAsBytevalues() throws IOException {
		JTableFile table = new JTableFile(
				new File("testFiles/testTableFiles/ff3_only_upper_and_lower.tbl"));
		File script = new File("testFiles/testScripts/Script12.txt");
		RandomAccessFile read;
		long fileLength;
		byte first;
		byte second;
		String third;
		byte[] r = new byte[1];
			
		/* Add empty pointer offset list. */
		ArrayList<Offset> offsets = new ArrayList<Offset>();
		
		/* Clear file contents. */
		if (testFile != null && testFile.exists()) {
			testFile.delete();
		}
		testFile = new File(fileUrl);
		testFile.createNewFile();
			
		ScriptInserter in = new JScriptInserter(testFile);
		read = new RandomAccessFile(testFile, "r");
		
		/* 
		 * Print 14 bytes, despite script being longer than that
		 * (reason is that hex tags take up 4 spaces, but translates
		 * to 1 byte. 
		 */
		err = in.insert(new Offset(0), 
					  new Offset(14), 
					  script, 
					  header,
					  offsets, 
					  PointerType._2Byte_littleEndian,
					  table,
					  0, (byte) 0);
		
		fileLength = read.length();
		
		
		assertEquals(14, fileLength);

		/* check the first byte in the file. */
		read.seek(0);
		read.read(r);
		first = r[0];
		
		/* check the second byte in the file. */
		read.seek(4);
		read.read(r);
		second = r[0];
		
		
		/* Read the third illegal hex "tag". */
		third = read(testFile, table, 10, 2);
		read.close();
		
		assertEquals(0, first);
		assertEquals(-1, second);
		assertEquals("G6", third);
		
	}
	
	
	@Test
	/**
	 * The script contains two japenese characters with the mapping 00 in
	 * the tablefile. Upon inserting the script, 00 00 should be written.
	 * 
	 * Since the default value for the file is 00, 02 is first written
	 * to confirm that 00 is being written over it.
	 */
	public void canInsertJapaneseCharacters() throws FileNotFoundException, IOException {
		JTableFile table = new JTableFile(
				new File("testFiles/testTableFiles/japanese.tbl"), "UTF-16");
		File script = new File("testFiles/testScripts/Script13.txt");
			
		/* Add empty pointer offset list. */
		ArrayList<Offset> offsets = new ArrayList<Offset>();
			
		testFile = new File(fileUrl);
		testFile.createNewFile();
			
		ScriptInserter in = new JScriptInserter(testFile);
		
		/* Print 02 to the first 5 bytes. */
		RandomAccessFile ran = new RandomAccessFile(testFile, "rw");
		byte[] o2 = new byte[]{02, 02, 02, 02, 02};
		ran.seek(0);
		ran.write(o2);

				
		err = in.insert(new Offset(0), 
					  new Offset(2), 
					  script,
					  header,
					  offsets, 
					  PointerType._2Byte_littleEndian,
					  table,
					  0, (byte) 0);	
		
		ran.seek(0);
		ran.read(o2);
		ran.close();
		
		/* First five bytes should now be 0,0,02,02,02. */
		byte[] o = new byte[]{0,0,02,02,02};
		assertTrue(Arrays.equals(o, o2));
	}
	
	
	@Test
	public void regularInsertIgnoresTabs() throws IOException {
		JTableFile table = new JTableFile(
			new File("testFiles/testTableFiles/ff3_upper_lower_numbers_newline_end.tbl"));
		File script = new File("testFiles/testScripts/Script18.txt");
			
		/* Add empty pointer offset list. */
		ArrayList<Offset> offsets = new ArrayList<Offset>();
			
		testFile = new File(fileUrl);
		testFile.createNewFile();
			
		ScriptInserter in = new JScriptInserter(testFile);
				
		err = in.insert(new Offset(0), 
					  new Offset(5), 
					  script,
					  header,
					  offsets, 
					  PointerType._2Byte_littleEndian,
					  table,
					  0, (byte) 0);
		
		String expected = "Tab.<END>" + separator;
			
		assertTrue(err.size() == 0);
		assertEquals(expected, read(testFile, table, 0, 5));
	}
	
	@Test
	public void regularInsertOmlyPrintsZeroesIfScriptIsEmpty() throws IOException {
		JTableFile table = new JTableFile(
			new File("testFiles/testTableFiles/ff3_only_upper_and_lower.tbl"));
		File script = new File("testFiles/testScripts/Script16.txt");
			
		/* Add empty pointer offset list. */
		ArrayList<Offset> offsets = new ArrayList<Offset>();
			
		testFile = new File(fileUrl);
		testFile.createNewFile();
			
		ScriptInserter in = new JScriptInserter(testFile);
				
		err = in.insert(new Offset(0), 
					  new Offset(5), 
					  script,
					  header,
					  offsets, 
					  PointerType._2Byte_littleEndian,
					  table,
					  0, (byte) 0);
		
		String expected = "<$00><$00><$00>";

		assertEquals(expected, read(testFile, table, 0, 3));
	}
	
	@Test
	public void canInsertPointersIntoSeparateFile() throws IOException {
		JTableFile table = new JTableFile(
				new File("testFiles/testTableFiles/ff3_only_upper_and_lower.tbl"));
		File script = new File("testFiles/testScripts/Script7.txt");
				
		/* Add pointer offset, with pointer "located" at 00 00. */
		ArrayList<Offset> offsets = new ArrayList<Offset>();
		offsets.add(new Offset(0));
				
		testFile = new File(fileUrl);
		testFile.createNewFile();

		pointerFile = new File("tempPointerPrint");
		pointerFile.createNewFile();
				
		ScriptInserter in = new JScriptInserter(testFile, pointerFile);
					
		err = in.insert(new Offset(6), 
						  new Offset(11), 
						  script,
						  header,
						  offsets, 
						  PointerType._2Byte_littleEndian,
						  table,
						  0, (byte) 0);
			
		String expectedText = "Clean";
		String expectedPointer = "<$06><$00>";

		assertEquals(expectedText, read(testFile, table, 6, 5));
		assertEquals(expectedPointer, read(pointerFile, table, 0, 2));
	}
	
	
	/**
	 * 
	 * Fixed length testing starts here.
	 * @throws IOException 
	 * 
	 */
	
	@Test
	public void padsShortStringsWithBytes() throws IOException {
		JTableFile table = new JTableFile(
				new File("testFiles/testTableFiles/ff3_upper_lower_numbers_newline_end.tbl"));
		File script = new File("testFiles/testScripts/Script15.txt");
			
		/* Add empty pointer offset list. */
		ArrayList<Offset> offsets = new ArrayList<Offset>();
		
		/* Clear file contents. */
		if (testFile != null && testFile.exists()) {
			testFile.delete();
		}
		testFile = new File(fileUrl);
		testFile.createNewFile();
			
		ScriptInserter in = new JScriptInserter(testFile);
		
		/* Print 10 bytes, despite scriptbeing longer than that. */
		byte jo = (byte) Integer.parseInt("FF", 16);
		err = in.insert(new Offset(0), 
					  new Offset(5), 
					  script, 
					  header,
					  offsets, 
					  PointerType._2Byte_littleEndian,
					  table,
					  5, jo);
		
		String expected = "123<$FF><$FF>";

		assertEquals(expected, read(testFile, table, 0, 5));
	}
	
	@Test
	public void cutsStringsLongerThanTheFixedLength() throws FileNotFoundException, IOException {
		JTableFile table = new JTableFile(
				new File("testFiles/testTableFiles/ff3_only_upper_and_lower.tbl"));
		File script = new File("testFiles/testScripts/Script15.txt");
			
		/* Add empty pointer offset list. */
		ArrayList<Offset> offsets = new ArrayList<Offset>();
		
		/* Clear file contents. */
		if (testFile != null && testFile.exists()) {
			testFile.delete();
		}
		testFile = new File(fileUrl);
		testFile.createNewFile();
			
		ScriptInserter in = new JScriptInserter(testFile);
		
		/* Print 10 bytes, despite scriptbeing longer than that. */
		byte jo = (byte) Integer.parseInt("FF", 16);
		err = in.insert(new Offset(0), 
					  new Offset(5), 
					  script, 
					  header,
					  offsets, 
					  PointerType._2Byte_littleEndian,
					  table,
					  2, jo);
		
		/* Read one extra byte, to make sure nothing else is inserted after. */
		String expected = "12<$00>";

		assertEquals(expected, read(testFile, table, 0, 3));
	}
	
	@Test
	public void doesNotWriteFixedlengthIfFileIsEmpty() throws IOException {
		JTableFile table = new JTableFile(
				new File("testFiles/testTableFiles/ff3_only_upper_and_lower.tbl"));
		File script = new File("testFiles/testScripts/Script16.txt");
			
		/* Add empty pointer offset list. */
		ArrayList<Offset> offsets = new ArrayList<Offset>();
		
		/* Clear file contents. */
		if (testFile != null && testFile.exists()) {
			testFile.delete();
		}
		testFile = new File(fileUrl);
		testFile.createNewFile();
			
		ScriptInserter in = new JScriptInserter(testFile);
		
		/* Print 10 bytes, despite scriptbeing longer than that. */
		byte jo = (byte) Integer.parseInt("FF", 16);
		err = in.insert(new Offset(0), 
					  new Offset(5), 
					  script, 
					  header,
					  offsets, 
					  PointerType._2Byte_littleEndian,
					  table,
					  4, jo);
		
		/* Read one extra byte, to make sure nothing else is inserted after. */
		String expected = "<$00><$00><$00><$00><$00>";

		assertEquals(expected, read(testFile, table, 0, 5));
	}
	
	@Test
	public void padsEmptySctringWithPadBytes() throws FileNotFoundException, IOException {
		JTableFile table = new JTableFile(
				new File("testFiles/testTableFiles/ff3_only_upper_and_lower.tbl"));
		File script = new File("testFiles/testScripts/Script17.txt");
			
		/* Add empty pointer offset list. */
		ArrayList<Offset> offsets = new ArrayList<Offset>();
		
		/* Clear file contents. */
		if (testFile != null && testFile.exists()) {
			testFile.delete();
		}
		testFile = new File(fileUrl);
		testFile.createNewFile();
			
		ScriptInserter in = new JScriptInserter(testFile);
		
		/* Print 10 bytes, despite scriptbeing longer than that. */
		byte jo = (byte) Integer.parseInt("FF", 16);
		err = in.insert(new Offset(0), 
					  new Offset(5), 
					  script, 
					  header,
					  offsets, 
					  PointerType._2Byte_littleEndian,
					  table,
					  4, jo);
		
		/* Read one extra byte, to make sure nothing else is inserted after. */
		String expected = "<$FF><$FF><$FF><$FF><$00>";

		assertEquals(expected, read(testFile, table, 0, 5));
	}
	
	
	@Test
	public void fixedLengthIgnoresTabs() throws FileNotFoundException, IOException {
		JTableFile table = new JTableFile(
				new File("testFiles/testTableFiles/ff3_only_upper_and_lower.tbl"));
		File script = new File("testFiles/testScripts/Script18.txt");
			
		/* Add empty pointer offset list. */
		ArrayList<Offset> offsets = new ArrayList<Offset>();
		
		/* Clear file contents. */
		if (testFile != null && testFile.exists()) {
			testFile.delete();
		}
		testFile = new File(fileUrl);
		testFile.createNewFile();
			
		ScriptInserter in = new JScriptInserter(testFile);
		
		/* Print 10 bytes, despite scriptbeing longer than that. */
		byte jo = (byte) Integer.parseInt("FF", 16);
		err = in.insert(new Offset(0), 
					  new Offset(5), 
					  script, 
					  header,
					  offsets, 
					  PointerType._2Byte_littleEndian,
					  table,
					  4, jo);
		
		/* Read one extra byte, to make sure nothing else is inserted after. */
		String expected = "Tab.";

		assertEquals(expected, read(testFile, table, 0, 4));
	}
	
	@After
	public void close() {
		if (!testFile.exists()) {
			System.out.println("Test file not created.");

		} else if (!testFile.delete()) {
			System.out.println("Could not delete test file.");
		}
		
		if (pointerFile != null && !pointerFile.exists()) {
			System.out.println("Pointer file not created.");
		} else if (pointerFile != null && !pointerFile.delete()) {
			System.out.println("Could not delete Pointer file.");
		}
	}
	
	/**
	 * Reads bytes into string from a file.
	 */
	private String read(File file, JTableFile table, int start, int bytes) {
		byte[] data = new byte[bytes];
		String ret = "";
		RandomAccessFile r = null;
		
		try {
			r = new RandomAccessFile(file, "r");
			r.seek(start);
			r.read(data);
			
			for (int i = Math.min(table.getHexMaxLength(), data.length); data.length > 0; i-- ) {
				String t = table.getString(ByteCalc.subArray(data, 0, i));
				if (t != null) {
					ret = ret + t;
					data = ByteCalc.subArray(data, i, data.length);
					i = Math.min(table.getHexMaxLength(), data.length) + 1;
				} else if(i == 1) {
					ret = ret + ScriptFormat.getByteTag(ByteCalc.subArray(data, 0, i));
					if (data.length > 1) {
						data = ByteCalc.subArray(data, 1, data.length);
					} else {
						data = new byte[0];
					}
					
					i++; // Keep i at 1
				}
			}
			return ret;
		} catch (FileNotFoundException e) {
			System.out.println("No file found.");
			return null;
		} catch (IOException e) {
			e.printStackTrace();
			
		} finally {
			if (r != null) {
				try {
					r.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		
		return ret;
	}
}
